#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>   // For waitpid
#include <pwd.h>
#include <errno.h>

#define MAX_PATH_LEN 256
#define MAX_HOSTNAME_LEN 100
#define MAX_INPUT_LEN 1024
#define MAX_ARGS 64

//打印用户名、主机名、当前路径
void display_prompt() {
    char hostname[MAX_HOSTNAME_LEN];
    char current_path[MAX_PATH_LEN];
    struct passwd *pw_entry;
    uid_t user_id;

    if (gethostname(hostname, sizeof(hostname)) == -1) {
        perror("gethostname 错误");
        strcpy(hostname, "unknown_host");
    }

    if (getcwd(current_path, sizeof(current_path)) == NULL) {
        perror("getcwd 错误");
        strcpy(current_path, "/unknown_path");
    }

    user_id = getuid();
    pw_entry = getpwuid(user_id);
    char *username = "unknown_user";
    if (pw_entry != NULL) {
        username = pw_entry->pw_name;
    }

    printf("%s@%s:%s$ ", username, hostname, current_path);
    fflush(stdout);
}

//解析命令和参数
int parse_input(char *input, char *argv[]) {
    int argc = 0;
    char *token;
    const char *delimiters = " \t\n";

    //分割字符串
    token = strtok(input, delimiters);
    while (token != NULL && argc < MAX_ARGS - 1) {
        argv[argc++] = token;
        token = strtok(NULL, delimiters);
    }
    argv[argc] = NULL;

    //返回解析出的参数个数
    return argc;
}

//识别并执行Shell的内置命令
int execute_builtin_command(char *argv[], int argc) {
    if (strcmp(argv[0], "exit") == 0) {
        printf("Shell 退出。\n");
        exit(EXIT_SUCCESS);
        // return 1;
    } 
    else if (strcmp(argv[0], "cd") == 0) {
        char *path_to_cd;
        char current_dir_before_cd[MAX_PATH_LEN];

        if (getcwd(current_dir_before_cd, sizeof(current_dir_before_cd)) == NULL) {
            strcpy(current_dir_before_cd, "unknown_before_cd");
        }

        if (argc < 2) {
            path_to_cd = getenv("HOME");
            if (path_to_cd == NULL) {
                fprintf(stderr, "cd: HOME 目录未设置\n");
                return 1;
            }
           
        } 
        else {
            path_to_cd = argv[1];
        }

        if (chdir(path_to_cd) != 0) {
            perror("cd 错误");
        } 
        
        return 1;
    } 
    else if (strcmp(argv[0], "help") == 0) {
        printf("一个简单的 Shell 程序\n");
        printf("支持的内置命令:\n");
        printf("  cd [目录]   - 更改当前工作目录\n");
        printf("  exit          - 退出 Shell\n");
        printf("  help          - 显示此帮助信息\n");
        printf("  help          - 显示此帮助信息\n");
        printf("支持管道 '|' 功能。\n");
        return 1;
    }
    return 0;
}


// 检查命令中是否包含管道符
// 返回值: 1 如果包含管道, 0 如果不包含
int has_pipe(char *argv[], int argc) {
    for (int i = 0; i < argc; i++) {
        if (argv[i] != NULL && strcmp(argv[i], "|") == 0) {
            return 1; // 找到管道符
        }
    }
    return 0; // 未找到管道符
}

// 新增函数：执行外部命令 (目前仅支持简单命令，无管道)
void execute_external_command(char *argv[]) {
    pid_t pid;
    int status;

    pid = fork(); // 创建子进程

    if (pid < 0) { // fork 失败
        perror("fork 错误");
        return; // 直接返回，让shell继续运行
    } else if (pid == 0) { // 子进程
        // 子进程执行命令
        if (execvp(argv[0], argv) == -1) {
            // 如果 execvp 返回，说明出错了
            fprintf(stderr, "%s: 命令未找到或执行错误\n", argv[0]);
            // perror(argv[0]); // perror 会提供更详细的系统错误信息
            exit(EXIT_FAILURE); // 子进程异常退出
        }
        // execvp 成功时不会执行到这里
    } else { // 父进程
        // 父进程等待子进程结束
        // waitpid(pid, &status, 0); // 等待指定的子进程
        // 或者简单地用 wait，它会等待任何一个子进程
        if (waitpid(pid, &status, 0) == -1) {
            perror("waitpid 错误");
        }
    }
}

// 处理带管道的命令 
void execute_pipe_command(char *original_argv[], int original_argc) {
    int pipe_fds[2]; // 0 for read, 1 for write
    pid_t pid1, pid2;
    int pipe_char_index = -1; // 管道符 '|' 在 argv 中的索引

    // 1. 找到管道符 '|' 的位置，并将 argv 分割
    for (int i = 0; i < original_argc; i++) {
        if (strcmp(original_argv[i], "|") == 0) {
            pipe_char_index = i;
            break;
        }
    }

    if (pipe_char_index == -1 || pipe_char_index == 0 || pipe_char_index == original_argc - 1) {
        fprintf(stderr, "无效的管道命令格式。\n");
        return;
    }

    // 创建分割后的参数列表
    // cmd1_argv 指向 original_argv 的开头，以 NULL 结尾（在'|'的位置）
    // cmd2_argv 指向 original_argv 中'|'之后的部分
    char **cmd1_argv = original_argv;
    original_argv[pipe_char_index] = NULL; // 将'|'替换为NULL，作为cmd1_argv的结束符
    char **cmd2_argv = &original_argv[pipe_char_index + 1];

    // 2. 创建管道
    if (pipe(pipe_fds) == -1) {
        perror("pipe 创建失败");
        return;
    }

    // 3. fork 第一个子进程 (执行 cmd1)
    pid1 = fork();
    if (pid1 < 0) {
        perror("fork (pid1) 失败");
        close(pipe_fds[0]);
        close(pipe_fds[1]);
        return;
    }

    if (pid1 == 0) { // 子进程1 (cmd1, e.g., ls)
        close(pipe_fds[0]); // 关闭管道读端，子进程1只写

        // 将标准输出重定向到管道写端
        if (dup2(pipe_fds[1], STDOUT_FILENO) == -1) {
            perror("dup2 (pid1 to STDOUT_FILENO) 失败");
            close(pipe_fds[1]);
            exit(EXIT_FAILURE);
        }
        close(pipe_fds[1]); // STDOUT_FILENO 现在是管道写端，关闭原始的

        // 执行 cmd1
        if (execvp(cmd1_argv[0], cmd1_argv) == -1) {
            fprintf(stderr, "%s: 命令未找到或执行错误\n", cmd1_argv[0]);
            exit(EXIT_FAILURE);
        }
        // execvp 成功不返回
    }

    // 4. fork 第二个子进程 (执行 cmd2)
    pid2 = fork();
    if (pid2 < 0) {
        perror("fork (pid2) 失败");
        close(pipe_fds[0]);
        close(pipe_fds[1]);
        waitpid(pid1, NULL, 0); // 等待第一个子进程结束以清理
        return;
    }

    if (pid2 == 0) { // 子进程2 (cmd2, e.g., more)
        close(pipe_fds[1]); // 关闭管道写端，子进程2只读

        // 将标准输入重定向到管道读端
        if (dup2(pipe_fds[0], STDIN_FILENO) == -1) {
            perror("dup2 (pid2 to STDIN_FILENO) 失败");
            close(pipe_fds[0]);
            exit(EXIT_FAILURE);
        }
        close(pipe_fds[0]); // STDIN_FILENO 现在是管道读端，关闭原始的

        // 执行 cmd2
        if (execvp(cmd2_argv[0], cmd2_argv) == -1) {
            fprintf(stderr, "%s: 命令未找到或执行错误\n", cmd2_argv[0]);
            exit(EXIT_FAILURE);
        }
        // execvp 成功不返回
    }

    // 5. 父进程 (Shell)
    // 父进程必须关闭管道的两端，否则管道可能不会正确结束
    close(pipe_fds[0]);
    close(pipe_fds[1]);

    // 等待两个子进程结束
    // 注意：等待顺序可能不重要，但最好都等待
    int status1, status2;
    if (waitpid(pid1, &status1, 0) == -1) {
        perror("waitpid (pid1) 错误");
    }
    if (waitpid(pid2, &status2, 0) == -1) {
        perror("waitpid (pid2) 错误");
    }

    // 恢复 original_argv 中被修改的'|'，如果后续还需要原始的 argv 的话
    // 对于单次执行，这一步可能不是必需的，但好的做法是恢复，以防万一
    // 但由于 strtok 本身就修改了 input_buffer，这里的恢复可能意义不大，
    // 除非 parse_input 也被修改为非破坏性的。
    // original_argv[pipe_char_index] = "|"; // 如果需要恢复
}



int main() {
    char input_buffer[MAX_INPUT_LEN];
    char *argv[MAX_ARGS];
    int argc;
    int is_builtin;

    while (1) {
        display_prompt();

        if (fgets(input_buffer, sizeof(input_buffer), stdin) == NULL) {
            printf("\nShell 退出。\n");
            break;
        }
        input_buffer[strcspn(input_buffer, "\n")] = 0;

        if (strlen(input_buffer) == 0) {
            continue;
        }
        
        argc = parse_input(input_buffer, argv);

        if (argc == 0) {
            continue;
        }

        is_builtin = execute_builtin_command(argv, argc);

        if (is_builtin) {
            continue;
        }

        // 不是内置命令，检查是否有管道
        if (has_pipe(argv, argc)) {
            // 如果有管道，调用管道处理函数 (目前是占位符)
            execute_pipe_command(argv, argc);
        } else {
            // 如果没有管道，执行简单外部命令
            execute_external_command(argv);
        }
    }

    return 0;
}
